//------------------------------------------------------------------------------
// X2D Game Engine
// Copyright (C) 2006, 2007, 2008 Janusz Kowalski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

// !! World should derive from Master. Create GameObject's with getWorld()
// stat statically casts master_. Then derive from them.
// !! Socket diconnection before destruction could be performed fully
// automatically.

#include "Box2D.h"

#include "imgSDL.h"
#include "RawVector.h"
#include "x2d_Form.h"
#include "x2d_Map.h"

#include <vector>

#include "x2d_BgLayer.h"
#include "global.h"

#include "x2d_Anim.h"

#include "x2tk_Graphics.h"

#include "x2d_ext.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace fs = boost::filesystem;

#ifndef X2D_NEW_H
#define X2D_NEW_H

namespace x2d {

enum {
  T_NONE, T_POLY_SHAPE, T_BODY, T_DRAW_POLY
};

const unsigned POLY_EH_FORMID = 59;
  
class World;

//-----------------------------------------------------------------------------
// EntityHandler class.
//-----------------------------------------------------------------------------

// EntityHandler handles one or more entities.

class EntityHandler {
  public:
    EntityHandler();
    ~EntityHandler();
    // Draws entity at the given (x,y) position.
    virtual void draw(GraphicContext &g, float32 x, float32 y, b2Body *body=0)=0;
    // Create entity at the given position.
    virtual EntityHandler *createEntity(int x, int y)=0;
};

class TestEntityHandler : public EntityHandler {
  private:
    Anim *anim_;
    World *world_;
  public:
    TestEntityHandler(World *world, unsigned formId);
    virtual void draw(GraphicContext &g, float32 x, float32 y, b2Body *body=0);
    // Create entity at the given position.
    virtual EntityHandler *createEntity(int x, int y);
};

class PolyEntityHandler : public EntityHandler {
  private:
    World *world_;
  public:
    PolyEntityHandler(World *world);
    virtual void draw(GraphicContext &g, float32 x, float32 y, b2Body *body=0);
    virtual EntityHandler *createEntity(int x, int y);
};

//-----------------------------------------------------------------------------
// FormList class
//-----------------------------------------------------------------------------

// FormList maps GameObject types (formId) to GameObject data, like image,
// animation, material, script, etc. This data is then used to create
// GameObject's.

class FormList {
  private:
    RawVector<EntityHandler*> entityHandlerPtrList_;
  public:
    FormList(World *world);
    ~FormList();
    //SDL_Surface *getLook(int formId);
    //Image &getLook(unsigned formId);
    int getPlane(int formId);
    int getNextFormId(int formId, int plane);
    int getPreviousFormId(int formId, int plane);
    
    // Create form.
    void createForm(World *world, unsigned formId, int x, int y);
    
    EntityHandler* getEntityHandler(unsigned formId);
    
    Uint32 getIdOfEntityHandler(EntityHandler *handler);
    EntityHandler *getEntityHandlerById(Uint32 id);
};

//-----------------------------------------------------------------------------
// Entity class
//-----------------------------------------------------------------------------

class World;

class Entity {
  private:
    double x_;
    double y_;
  public:
    Entity(World *world);
    ~Entity();
    virtual double getX() {return x_;};
    virtual double getY() {return y_;};
    inline void setX(double x) {x_ = x;}
    inline void setY(double y) {y_ = y;}
    virtual double getVelX() {return 0;}
    virtual double getVelY() {return 0;}
    virtual void draw(GraphicContext &g, float x, float y);
    
    // This function is called for every object we collide with.
    // true - accept collision
    // false - ignore collision
    virtual bool collision(int formId);
    
    // 100Hz time callback
    virtual void timeCb();
  protected:
    World *world_;
};


//-----------------------------------------------------------------------------
// Physical entity base class. Physical entities are derived from this class.
//-----------------------------------------------------------------------------

// Drawing is left to the implementer. This class handles entity's physical
// body.

class PhysicalEntity : public Entity {
  protected:
    b2Body *body_;
  public:
    PhysicalEntity(World *world, float32 x, float32 y);
    ~PhysicalEntity();
    virtual double getX();
    virtual double getY();
    virtual void draw(GraphicContext &g, float x, float y);
    virtual void timeCb();

    virtual void contact(b2Body &body, b2Shape &shape1, b2Shape &shape2);
};

//-----------------------------------------------------------------------------
// Physical circle
//-----------------------------------------------------------------------------

class PhyCircleEnt : public PhysicalEntity {
  private:
    float32 r_;
    Image img_;
  public:
    // x,y and r are in pixels
    PhyCircleEnt(World *world, float32 x, float32 y, float32 r);
    virtual void draw(GraphicContext &g, float x, float y);
    virtual void contact(b2Body &body, b2Shape &shape1, b2Shape &shape2);
};

//-----------------------------------------------------------------------------
// Physical test entity
//-----------------------------------------------------------------------------

class PhyEnt : public Entity {
  private:
    b2Body *body_;
    Image img_;
    bool right_;
  public:
    PhyEnt(World *world);
    ~PhyEnt();
    virtual double getX();
    virtual double getY();
    virtual Image& getLook();
    virtual void draw(GraphicContext &g, int x, int y);
    virtual void timeCb();
};

//-----------------------------------------------------------------------------
// Player Entity
//-----------------------------------------------------------------------------

class PlayerEnt : public Entity {
  private:
    Image img_;
    bool goLeft_;
    bool goRight_;
    bool jump_;
    
    float xVelocity_;
    float yVelocity_;
    
    bool onTheGround_;
  public:
    PlayerEnt(World *world);
    ~PlayerEnt();
    virtual Image &getLook();
    
    virtual double getVelX() {return xVelocity_;}
    virtual double getVelY() {return yVelocity_;}
    
    inline void goLeft(bool value) {goLeft_ = value;}
    inline void goRight(bool value) {goRight_ = value;}
    inline void jump(bool value) {jump_ = value;}
    
    virtual void timeCb();
};

//-----------------------------------------------------------------------------
// Something bad.
//-----------------------------------------------------------------------------

class SomethingBadEnt : public Entity {
  private:
    Anim *sbLeft_;
    Anim *sbRight_;
    bool lookLeft_;
    
    float xVelocity_;
    float yVelocity_;
  public:
    SomethingBadEnt(World *world);
    ~SomethingBadEnt();
    
    virtual Image &getLook();
    
    virtual void timeCb();
};

//-----------------------------------------------------------------------------
// World's contact listener.
//-----------------------------------------------------------------------------

class ContactListener : public b2ContactListener {
  private:
    World *world_;
  public:
    ContactListener(World *world);

    void Add(b2ContactPoint *point);

    void Persist(b2ContactPoint *point);

    void Remove(b2ContactPoint *point);
};



//-----------------------------------------------------------------------------
// Object class 
//-----------------------------------------------------------------------------

const unsigned FLAG_DESTROY = 0x01;
const unsigned FLAG_VISITED = 0x02;
const unsigned FLAG_DESTRUCTOR_OK = 0x04;
const unsigned FLAG_VALID_ID = 0x08;

class Object;
class World;

bool xconnect(Object *objectA, int socketIdA, Object *objectB);
void xdisconnect(Object *objectA, int socketIdA, Object *objectB);

class Object {
  private:
    struct ObjectPlusSocketId {
      Object *object;
      int socketId;
      ObjectPlusSocketId(Object *object_, int socketId_) : object(object_), socketId(socketId_) { }
      bool operator== (const ObjectPlusSocketId &right) {
        return (object==right.object) && (socketId==right.socketId);
      }
    };
    RawVector<ObjectPlusSocketId> relList_;
    World &master_;
    unsigned flags_;
    Object *next_;
    Object *prev_;
    int id_; // Valid only sometimes.
  protected:
    // These functions are used by World object.
    virtual bool connectToSocket(int id, Object *object);
    virtual void disconnectFromSocket(int id, Object *object);
    virtual void createRelationTo(Object *object, int id);
    virtual void destroyRelationTo(Object *object, int id);
    virtual void beforeDestruction();
  public:
    Object(World &master);
    ~Object();
    void scheduleForDestruction();
    virtual const char *socketName(int socketId);
    virtual int socketCount();
    virtual int connectionCount(int socketId);
    virtual Object* getConnected(int socketId, int i);
    Object *nextObject();
    Object *prevOBject();
    
    virtual void store(std::ofstream &stream);
    virtual void load(std::ifstream &stream);
    
    virtual int getTypeId() = 0;

    friend bool xconnect(Object *objectA, int socketIdA, Object *objectB);
    friend void xdisconnect(Object *objectA, int socketIdA, Object *objectB);
    friend class World;
};

//-----------------------------------------------------------------------------
// World class
//-----------------------------------------------------------------------------

// Function used by World to draw itself.
// Using a function gives possibility to scale visible area by the object
// looking at the World object.
typedef void (*LookCb) (int x, int y, SDL_Surface *surf, void *data);

// When destroyed, World object destroys all its entities.
class World {
  private:
    Map *map_;
    RawVector<Entity*> entList_;
    FormList *formList_;
    SDL_Surface *background_;
    void drawBackground(int dx, int dy, int w, int h, LookCb fn, void *data);
    RawVector<BgLayer> bgLayerList_;
    RawVector<BgScrap> bgScrapList_;
    bool pause_;
    // Physical world.
    b2World *world_;
    // And the contact listener for it.
    ContactListener *contactListener_;
    //-- New elements --
    Object *firstObject_;
    void destroyAll();
    
    // Internal functions called by objects.
    void addObject(Object *object);
    void removeObject(Object *object);
    
  public:
    Object *firstObject();
    
    void destructionPass();
    
    void load(const fs::path &path);
    void save(const fs::path &path);
    
    friend class Object;
    
  public:
    World(Map *map = 0);
    ~World();
    void add(Entity *ent);
    void remove(Entity *ent);
    
    // Get images of objects inside the rectangle using a callback function.
    void getRect(LookCb fn, void *data, int x, int y, int w, int h);

    // Get pointer to the form list.
    inline FormList *getFormList() {return formList_;}
    
    // Get pointer to the map.
    inline Map *getMap() {return map_;}

    // Get pointer to the background surface.
    inline SDL_Surface *getBackground() {return background_;}
    
    // Get the list of background layers.
    inline RawVector<BgLayer> *getBgLayerList() {return &bgLayerList_;}
    
    inline RawVector<BgScrap> *getBgScrapList() {return &bgScrapList_;}
    
    // Get the list of entities
    inline RawVector<Entity*> *getEntList() {return &entList_;}
    
    // Create entity at the given pixel position.
    void createEntity(unsigned formId, int x, int y);
    
    void timeCb();
    
    void setPause(bool v) {pause_=v;}
    
    inline b2World *getB2World() { return world_; }
};

//-----------------------------------------------------------------------------
// DrawPolyObject class declaration.
//-----------------------------------------------------------------------------

class DrawPolyObject : public Object {
  public:
    DrawPolyObject(World &master) : Object(master) { }
    int getTypeId();
    void draw(GraphicContext &g, b2PolygonShape *poly, float32 x, float32 y);
};

//-----------------------------------------------------------------------------
// BodyObject class declaration.
//-----------------------------------------------------------------------------

class BodyObject : public Object {
  private:
    b2Body *body_;
  public:
    BodyObject(World &master);
    
    // Create body using body definition. This constructor creates real b2Body.
    BodyObject(World &master, b2BodyDef *bodyDef, bool isStatic);
    
    int getTypeId();
    
    b2Body *getBody();
};

//-----------------------------------------------------------------------------
// PolyShapeObject class declaration.
//-----------------------------------------------------------------------------

class PolyShapeObject : public Object {
  private:
    b2PolygonDef *polyDef_;
    b2PolygonShape *polyShape_;
    BodyObject *bodyObject_;
    DrawPolyObject *drawPolyObject_;
  protected:
    virtual bool connectToSocket(int socketId, Object *object);
    virtual void disconnectFromSocket(int socketId, Object *object);
    virtual void beforeDestruction();
  public:
    PolyShapeObject(World &master);
    PolyShapeObject(World &master, const b2PolygonDef &polyDef);
    ~PolyShapeObject();
    
    virtual const char *socketName(int socketId);
    
    int getTypeId();
    
    // 3 - body, drawing, collision.
    virtual int socketCount();
    virtual int connectionCount(int socketId);
    virtual Object* getConnected(int socketId, int i);
    void draw(GraphicContext &g, float32 x, float32 y);
};

} // namespace x2d

#endif
